package com.ruoyi.mywork.aop;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Stream;

/**
 * Location模块Controller层日志切面
 */
@Aspect
@Component
@Slf4j
public class MyLogAspect {

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");

    /**
     * 定义切点 - 只过滤com.ruoyi.location.controller包下的所有方法，但排除page包下的方法
     */
    @Pointcut("execution(* com.ruoyi.mywork.controller..*.*(..)) && !execution(* com.ruoyi.mywork.controller.page..*.*(..))")
    public void locationControllerPointcut() {}

    /**
     * 处理请求前执行
     */
    @Before("locationControllerPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        TIME_THREADLOCAL.set(System.currentTimeMillis());

        try {
            // 获取当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            // 记录请求信息
            log.info("==================== Request Start ====================");
            log.info("URL: {} {}", request.getMethod(), request.getRequestURL().toString());

            // 获取GET参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!parameterMap.isEmpty()) {
                log.info("GET Parameters: {}", JSONObject.toJSONString(parameterMap));
            }

            // 获取POST参数（排除文件上传等特殊类型）
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                Object[] logArgs = Stream.of(args)
                        .filter(arg -> !isFilterObject(arg))
                        .toArray();
                if (logArgs.length > 0) {
                    log.info("POST Parameters: {}", JSONObject.toJSONString(logArgs));
                }
            }
        } catch (Exception e) {
            log.error("记录请求参数异常", e);
        }
    }

    /**
     * 环绕通知，处理返回结果
     */
    @Around("locationControllerPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        
        // 如果返回结果不是AjaxResult，将其包装成AjaxResult
        if (result != null && !(result instanceof AjaxResult)) {
            result = AjaxResult.success("操作成功", result);
        }
        
        return result;
    }

    /**
     * 处理完请求后执行
     */
    @AfterReturning(pointcut = "locationControllerPointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        try {
            // 记录返回结果
            if (result != null) {
                log.info("Response: {}", JSONObject.toJSONString(result));
            } else {
                log.info("Response: null");
            }

            // 记录执行时间
            long costTime = System.currentTimeMillis() - TIME_THREADLOCAL.get();
            log.info("Time Cost: {} ms", costTime);
            log.info("==================== Request End ====================\n");
        } catch (Exception e) {
            log.error("记录响应结果异常", e);
        } finally {
            TIME_THREADLOCAL.remove();
        }
    }

    /**
     * 判断是否需要过滤的对象
     */
    private boolean isFilterObject(final Object o) {
        if (o == null) {
            return true;
        }

        Class<?> clazz = o.getClass();

        // 处理数组类型
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }

        // 处理集合类型
        if (Collection.class.isAssignableFrom(clazz)) {
            Collection<?> collection = (Collection<?>) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        }

        // 处理Map类型
        if (Map.class.isAssignableFrom(clazz)) {
            Map<?, ?> map = (Map<?, ?>) o;
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                return entry.getValue() instanceof MultipartFile;
            }
        }

        // 过滤掉特殊类型
        return o instanceof MultipartFile
            || o instanceof HttpServletRequest
            || o instanceof HttpServletResponse
            || o instanceof ServletRequest
            || o instanceof ServletResponse
            || o instanceof BindingResult;
    }
}